﻿/*
	wp8libflac project
	© Alovchin, 2014
	*/

#pragma once

#include <stream_decoder.h>

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;

#include "FlacSample.h"
#include "FlacStreamInfo.h"

INTEROP_NAMESPACE_BEGIN

/// <summary>
/// Describes FLAC stream decoder wrapper.
/// </summary>
public ref class FlacDecoder sealed
{
	static FLAC__StreamDecoderReadStatus read_callback(
		const FLAC__StreamDecoder* decoder, FLAC__byte buffer[], size_t* bytes, void* client_data);

	static FLAC__StreamDecoderWriteStatus write_callback(
		const FLAC__StreamDecoder* decoder,
		const FLAC__Frame* frame,
		const FLAC__int32* const buffer[],
		void* client_data);

	static FLAC__StreamDecoderSeekStatus seek_callback(
		const FLAC__StreamDecoder* decoder, FLAC__uint64 absolute_byte_offset, void* client_data);

	static FLAC__StreamDecoderTellStatus tell_callback(
		const FLAC__StreamDecoder* decoder, FLAC__uint64* absolute_byte_offset, void* client_data);

	static FLAC__StreamDecoderLengthStatus length_callback(
		const FLAC__StreamDecoder* decoder, FLAC__uint64* stream_length, void* client_data);

	static void metadata_callback(
		const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data);

	static FLAC__bool eof_callback(const FLAC__StreamDecoder* decoder, void* client_data);

	static void error_callback(
		const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErrorStatus status, void* client_data);

	void EnsureMetadataRead();

public:
	FlacDecoder() : m_metadataRead(false), m_pFile(nullptr), m_currentSample(nullptr)
	{
		this->m_flacDecoder = FLAC__stream_decoder_new();
		FLAC__stream_decoder_set_metadata_respond(this->m_flacDecoder, FLAC__METADATA_TYPE_PICTURE);
		FLAC__stream_decoder_set_metadata_respond(this->m_flacDecoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
	}

	/// <summary>
	/// Decoder error event.
	/// </summary>
	event DecoderErrorHandler^ ErrorCallback;

	/// <summary>
	/// Initializes decoder with specified FLAC file stream.
	/// </summary>
	/// <param name="fileName">FLAC file name.</param>
	void Initialize(String^ fileName);

	/// <summary>
	/// Reads FLAC metadata and returns FLAC stream info.
	/// </summary>
	/// <returns>FLAC stream info.</returns>
	FlacStreamInfo^ GetStreamInfo();

	/// <summary>
	/// Reads the next sample from FLAC stream.
	/// </summary>
	/// <returns>Sample, or null if stream end is reached.</returns>
	FlacSample^ GetSample();

	/// <summary>
	/// Returns decoder's current position in FLAC stream.
	/// </summary>
	property int64 Position
	{
		int64 get() { return this->m_pFile != nullptr ? (int64)_ftelli64(this->m_pFile) : 0; }
	}

	/// <summary>
	/// Sets decoder's current position in FLAC stream.
	/// </summary>
	/// <param name="position">Absolute position in stream (in bytes).</param>
	void Seek(int64 position);

	/// <summary>
	/// Closes the file and resets the decoder.
	/// </summary>	
	void Close();

	/// <summary>
	/// Frees the decoder and closes the stream.
	/// </summary>
	virtual ~FlacDecoder()
	{
		this->Close();
		FLAC__stream_decoder_delete(this->m_flacDecoder);
	}

private:
	FLAC__StreamDecoder* m_flacDecoder;

	FILE*	m_pFile;
	bool	m_metadataRead;

	FlacStreamInfo^	m_streamInfo;
	FlacSample^		m_currentSample;
};

INTEROP_NAMESPACE_END
